#include "cJSON.h"
#include "esp_err.h"
#include "esp_http_server.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>

#define BASE_PATH "/littlefs"

#define TAG "http_server"

/* 列出文件 */
#include "cJSON.h"

/* 列出文件 */
static esp_err_t list_files_handler(httpd_req_t *req) {
    cJSON *root = cJSON_CreateObject();
    if (!root) {
        ESP_LOGE(TAG, "Failed to create JSON root");
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }

    cJSON *files_array = cJSON_AddArrayToObject(root, "files");
    if (!files_array) {
        cJSON_Delete(root);
        ESP_LOGE(TAG, "Failed to create JSON array");
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }

    struct dirent *entry;
    DIR *dir = opendir(BASE_PATH);
    if (!dir) {
        ESP_LOGE(TAG, "Failed to open directory");
        cJSON_Delete(root);
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }

    while ((entry = readdir(dir)) != NULL) {
        cJSON_AddItemToArray(files_array, cJSON_CreateString(entry->d_name));
    }
    closedir(dir);

    // 将 cJSON 转换为字符串
    char *json_response = cJSON_PrintUnformatted(root);
    if (!json_response) {
        ESP_LOGE(TAG, "Failed to print JSON");
        cJSON_Delete(root);
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }

    // 发送 JSON 响应
    httpd_resp_set_type(req, "application/json");
    httpd_resp_send(req, json_response, HTTPD_RESP_USE_STRLEN);

    // 释放 JSON 资源
    cJSON_Delete(root);
    free(json_response);

    return ESP_OK;
}

/* 下载文件 */
static esp_err_t download_file_handler(httpd_req_t *req) {
    char filepath[128];
    char filename[64];

    ESP_LOGI(TAG, "Download request: %s", req->uri);

    /* 获取完整的查询参数字符串 */
    char query[128];
    if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) {
        ESP_LOGI(TAG, "Query string: %s", query);

        /* 从查询字符串中解析 filename 参数 */
        if (httpd_query_key_value(query, "filename", filename, sizeof(filename)) != ESP_OK) {
            ESP_LOGE(TAG, "Filename parameter missing");
            httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Missing filename parameter");
            return ESP_FAIL;
        }
    } else {
        ESP_LOGE(TAG, "Failed to get query string");
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request");
        return ESP_FAIL;
    }

    /* 确保文件名不包含路径遍历攻击字符 */
    if (strchr(filename, '/') || strchr(filename, '\\')) {
        ESP_LOGE(TAG, "Invalid filename: %s", filename);
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename");
        return ESP_FAIL;
    }

    /* 组合完整路径 */
    snprintf(filepath, sizeof(filepath), "%s/%s", BASE_PATH, filename);
    ESP_LOGI(TAG, "Opening file: %s", filepath);

    /* 打开文件 */
    FILE *file = fopen(filepath, "r");
    if (!file) {
        ESP_LOGE(TAG, "Failed to open file: %s", filepath);
        httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File not found");
        return ESP_FAIL;
    }

    /* 设置 HTTP 头部，确保 PC 端下载的文件名正确 */
    httpd_resp_set_type(req, "application/octet-stream");
    char content_disp[128];
    snprintf(content_disp, sizeof(content_disp), "attachment; filename=\"%s\"", filename);
    httpd_resp_set_hdr(req, "Content-Disposition", content_disp);

    /* 发送文件内容 */
    char buffer[512];
    size_t read_bytes;
    while ((read_bytes = fread(buffer, 1, sizeof(buffer), file)) > 0) {
        httpd_resp_send_chunk(req, buffer, read_bytes);
    }
    fclose(file);
    httpd_resp_send_chunk(req, NULL, 0);

    ESP_LOGI(TAG, "File sent successfully: %s", filename);
    return ESP_OK;
}

/* 删除文件 */
static esp_err_t delete_file_handler(httpd_req_t *req) {
    char buf[1024];
    int ret = httpd_req_recv(req, buf, sizeof(buf) - 1);

    if (ret <= 0) {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request body");
        return ESP_FAIL;
    }
    buf[ret] = '\0';
    ESP_LOGI(TAG, "Delete request: %s", buf);

    /* 解析 JSON */
    cJSON *root = cJSON_Parse(buf);
    if (!root) {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON format");
        return ESP_FAIL;
    }

    cJSON *files_array = cJSON_GetObjectItem(root, "files");
    if (!cJSON_IsArray(files_array)) {
        cJSON_Delete(root);
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid file list");
        return ESP_FAIL;
    }

    /* 记录删除成功和失败的文件 */
    cJSON *result = cJSON_CreateObject();
    cJSON *success_list = cJSON_AddArrayToObject(result, "deleted");
    cJSON *failed_list = cJSON_AddArrayToObject(result, "failed");

    /* 遍历文件列表 */
    for (int i = 0; i < cJSON_GetArraySize(files_array); i++) {
        cJSON *file_item = cJSON_GetArrayItem(files_array, i);
        if (!cJSON_IsString(file_item))
            continue;

        char filepath[128];
        snprintf(filepath, sizeof(filepath), "%s/%s", BASE_PATH, file_item->valuestring);
        ESP_LOGI(TAG, "Deleting file: %s", filepath);

        /* 删除文件 */
        if (remove(filepath) == 0) {
            ESP_LOGI(TAG, "File deleted successfully: %s", file_item->valuestring);
            cJSON_AddItemToArray(success_list, cJSON_CreateString(file_item->valuestring));
        } else {
            ESP_LOGE(TAG, "Failed to delete file: %s", file_item->valuestring);
            cJSON_AddItemToArray(failed_list, cJSON_CreateString(file_item->valuestring));
        }
    }

    /* 释放 JSON 资源 */
    cJSON_Delete(root);

    /* 返回 JSON 响应 */
    char *json_response = cJSON_PrintUnformatted(result);
    httpd_resp_set_type(req, "application/json");
    httpd_resp_send(req, json_response, HTTPD_RESP_USE_STRLEN);

    /* 释放 JSON 资源 */
    cJSON_Delete(result);
    free(json_response);

    return ESP_OK;
}

/* 上传 OTA 文件 */
static esp_err_t upload_ota_file_handler(httpd_req_t *req) {
    char filename[64] = {0};
    char filepath[128] = {0};

    // 获取 filename 参数
    char query[128];
    if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) {
        if (httpd_query_key_value(query, "filename", filename, sizeof(filename)) != ESP_OK) {
            ESP_LOGE(TAG, "Missing filename parameter");
            httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Missing filename parameter");
            return ESP_FAIL;
        }
    } else {
        ESP_LOGE(TAG, "Failed to parse query");
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request");
        return ESP_FAIL;
    }

    // 防止路径穿越
    if (strchr(filename, '/') || strchr(filename, '\\')) {
        httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid filename");
        return ESP_FAIL;
    }

    snprintf(filepath, sizeof(filepath), "%s/%s", BASE_PATH, filename);
    FILE *file = fopen(filepath, "wb");
    if (!file) {
        ESP_LOGE(TAG, "Failed to open file for writing: %s", filepath);
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to open file");
        return ESP_FAIL;
    }

    // 接收数据并写入文件
    char buffer[1024];
    int received;
    while ((received = httpd_req_recv(req, buffer, sizeof(buffer))) > 0) {
        fwrite(buffer, 1, received, file);
    }

    fclose(file);

    if (received < 0) {
        ESP_LOGE(TAG, "Receive error");
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Receive error");
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "File uploaded successfully: %s", filepath);
    httpd_resp_sendstr(req, "OTA file uploaded successfully");
    return ESP_OK;
}

/* 启动 HTTP 服务器 */
esp_err_t http_server_start(void) {
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    httpd_handle_t server = NULL;

    if (httpd_start(&server, &config) == ESP_OK) {
        static const httpd_uri_t list_files = {.uri = "/files", .method = HTTP_GET, .handler = list_files_handler};
        static const httpd_uri_t download_file = {.uri = "/download", .method = HTTP_GET, .handler = download_file_handler};
        static const httpd_uri_t delete_file = {.uri = "/delete", .method = HTTP_POST, .handler = delete_file_handler};
        static const httpd_uri_t upload_ota = {.uri = "/upload_ota", .method = HTTP_POST, .handler = upload_ota_file_handler};

        httpd_register_uri_handler(server, &list_files);
        httpd_register_uri_handler(server, &download_file);
        httpd_register_uri_handler(server, &delete_file);
        httpd_register_uri_handler(server, &upload_ota);

        return ESP_OK;
    }
    return ESP_FAIL;
}
